﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Manager : MonoBehaviour {

	// Properties
	//

	public ManagerHabitant ManagerHabitant;
	public ManagerEvenements ManagerEvent;
	public bool eventHappened = false;

	public float Building = 90;
	public float Population = 100;
	public float Mood = -70;

	public float maxPop = 1000f;
	public float minPop = 0f;
	public float maxBuilding = 1000f;
	public float minBuilding = 70f;
	public float maxMood = 350f;
	public float minMood = -250f;

	public float moodInertiaDuration = 0f;
	public float moodInertiaValue = 0f;

	// Population multiplier factor markoffs
	public float minBatPopFactor = 0.9f;
	public float maxBatPopFactor = 1.1f;
	public float minFactor = 0.8f;
	public float maxFactor = 1.2f;
	public float minMoodFactor = 0.1f;
	public int[] moodThreshold;

	// Inertia
	public int DegradationBuilding = -15;
	public int InertiaMood = -10;
	public int consecutiveBuildFactor = -10;

	// Grow
	public int BuildGrow = 150;
	public int ConsecutiveBuilds = 0;
	public int TimeFactor = 5;


	// Game Turn
	public int MaxTurn = 10;
	public int CurrentTurn = 1;

	// Actions
	public enum Actions {Build, Mood, Time};
	private List<Actions> LastActions =  new List<Actions>();

	// GUI variables
	private bool pause = false;
	public GameObject ingameGUIContainer;
	public GameObject endgameGUIContainer;
	public GameObject turn;
	public GameObject choice;
	public GameObject randomEvent;
	public GameObject GUIObjectives;
	public GUITexture popValue;
	public GUITexture buildValue;
	public GUITexture moodValue;
	public GUIText endPopValue;
	public GUIText endBuildValue;
	public GUIText endMoodValue;

	// Objectifs
	public List<Objectifs> Objectifs = new List<Objectifs> ();

	//Methods
	//
	//
	public void Start()
	{
		this.ManagerHabitant.UpdateNombreHabitantInstant();
		StartCoroutine ("TurnToChoiceGUI");

		// Reset the achievements to 0, otherwise Unity keep it stored in the prefab !
		foreach(Objectifs o in this.Objectifs){
			o.Done = false;
		}

	}

	public IEnumerator TurnToChoiceGUI()
	{
		yield return new WaitForSeconds(2f);
		turn.SetActive (false);
		yield return new WaitForSeconds(0.2f);
		choice.SetActive(true);
	}

	public IEnumerator ShowRandomEvent(string[] container)
	{
		yield return new WaitForSeconds (3f);
		randomEvent.transform.GetChild(0).guiText.text = container[0];
		randomEvent.transform.GetChild(1).guiText.text = container[1];
		randomEvent.SetActive (true);
	}

	public void ShowObjectives(string[] container)
	{
		this.GUIObjectives.transform.GetChild(0).guiText.text = container[0];
		this.GUIObjectives.transform.GetChild(1).guiText.text = container[1];
		this.GUIObjectives.SetActive (true);
	}

	public void ActionBuild()
	{
		choice.SetActive (false);
		this.Building += this.BuildGrow;

		if (CurrentTurn < MaxTurn)
			this.ManagerEvent.ApplyRandomEvent();

		this.Inertia();
		this.CheckChange();

		this.LastActions.Insert(0, Actions.Build);
	}

	public void ActionMood()
	{
		choice.SetActive (false);
		int lastMoods = this.LastMoods();
		float popFactor = this.Building / this.Population;
		float moodLevel = getMoodFactor ();

		if (popFactor > 1)
		{
			if (moodLevel >= 0)
			{
				this.Mood += 100;
				this.moodInertiaValue = 10 * moodLevel;
				this.moodInertiaDuration = 1;
			}
			else
			{
				this.Mood += 100 + Mathf.Abs(moodLevel) * 10;
				this.moodInertiaValue = 20 * Mathf.Abs(moodLevel);
				this.moodInertiaDuration = 2;
			}
		}
		else
		{
			if (moodLevel >= 0)
			{
				this.Mood += 80f;
				this.moodInertiaValue = 10 * moodLevel;
				this.moodInertiaDuration = 1;
			}
			else
			{
				this.Mood += 80f + Mathf.Abs(moodLevel) * 10;
				this.moodInertiaValue = 10 * Mathf.Abs(moodLevel);
				this.moodInertiaDuration = 2;
			}
		}

		if (CurrentTurn < MaxTurn - 1)
			this.ManagerEvent.ApplyRandomEvent();

		this.Inertia();

		this.CheckChange();

		this.LastActions.Insert(0, Actions.Mood);
	}

	public void ActionTime()
	{
		choice.SetActive (false);
		float popDiff = this.Building - this.Population;
		if (popDiff > 0)
		{
			this.Population = Mathf.Min(this.Population * 1.5f, this.Population + popDiff * 1.1f);
		}
		else
		{
			this.Population *= 1.1f;
			this.Mood -= 50f;
		}

		this.Inertia ();

		if (CurrentTurn < MaxTurn)
			this.ManagerEvent.ApplyRandomEvent();

		this.CheckChange();
		this.LastActions.Insert(0, Actions.Time);
	}

	public void CheckChange()
	{
		string[] tmp = new string[] {"depressed", "sad", "neutral", "happy", "jubilant"};
		int i;
		for (i = 0; i < moodThreshold.GetLength(0); i++)
		{
			if (this.Mood < moodThreshold[i])
				break;
		}

		this.CurrentTurn ++;

		this.ManagerHabitant.UpdateNombreHabitant();
		this.ManagerHabitant.UpdateMoodHabitants();

		turn.guiText.text = "Turn " + CurrentTurn.ToString ();
		buildValue.transform.localPosition = new Vector3 (-0.49f +0.05f*(Mathf.Floor(Mathf.Max(this.Building / 100, 1))), 0, 3);
		buildValue.transform.localScale = new Vector3 ((Mathf.Floor(Mathf.Max(this.Building / 100, 1))) / 10, 1, 1);
		popValue.transform.localPosition = new Vector3 (-0.49f +0.0005f*this.Population, 0, 3);
		popValue.transform.localScale = new Vector3 (this.Population / this.maxPop, 1f, 1f);

		moodValue.transform.localPosition = new Vector3 (0.22f * this.getMoodFactor(), 0, 3);
	
		GameObject.Find ("Village").GetComponent<Village>().ChangeLevel (Mathf.FloorToInt((this.Building / 100)));
		if(this.CurrentTurn > this.MaxTurn)
			StartCoroutine ("EndGame", tmp[i]);
		else
			StartCoroutine ("NewTurn");
	}

	public IEnumerator EndGame(string mood)
	{
		yield return new WaitForSeconds (3f);
		ingameGUIContainer.SetActive(false);
		GameObject.Find ("Main Camera").GetComponent<Animator> ().Play ("EndGame");
		yield return new WaitForSeconds (11f);

		// Check the objectives
		yield return StartCoroutine("CheckNewObjectives");

		endPopValue.text = Mathf.Floor(this.Population).ToString() + " inhabitants";
		endBuildValue.text = "Level " + (Mathf.Floor(this.Building / 100) + 1).ToString() + " buildings";
		endMoodValue.text = mood + " people";
		endgameGUIContainer.SetActive(true);



	}
	
	public IEnumerator NewTurn()
	{
		if (this.eventHappened)
		{
			yield return new WaitForSeconds (6f);
			eventHappened = false;
		}
		else
			yield return new WaitForSeconds (3f);
		randomEvent.SetActive (false);
		turn.SetActive (true);
		StartCoroutine ("TurnToChoiceGUI");
	}

	// Private methods
	//
	//

	private int LastMoods()
	{
		int res = 0;
		for(int i = 0;i< this.LastActions.Count; i++)
		{
			if(this.LastActions[i] == Actions.Mood)
				res ++;
			else
				break;
		}
		return res;
	}

	private int LastBuilds()
	{
		int res = 0;
		for(int i = 0;i< this.LastActions.Count; i++)
		{
			if(this.LastActions[i] == Actions.Build)
				res ++;
			else
				break;
		}
		return res;
	}

	private int getMoodFactor()
	{
		if (this.Mood < -200)
			return -2;
		else if (this.Mood < -100)
			return -1;
		else if (this.Mood < 100)
			return 0;
		else if (this.Mood < 300)
			return 1;
		else
			return 2;
	}

	private void Inertia()
	{
		float moodLevel = this.getMoodFactor();
		float popFactor = this.Building / this.Population;

		if (popFactor > 1)
			this.Population *= this.MarkOff((popFactor + moodLevel * 0.1f), 1.1f, 1);
		else
			this.Population *= this.MarkOff((popFactor + moodLevel * 0.1f), 1, 0.7f);

		this.Mood += InertiaMood + this.moodInertiaValue;
		if (moodInertiaDuration == 1)
		{
			this.moodInertiaValue = 0;
			this.moodInertiaDuration = 0;
		}
		else if (this.moodInertiaValue == 2)
		{
			this.moodInertiaValue /= 2f;
			this.moodInertiaDuration = 1;
		}
		this.Mood = this.MarkOff(this.Mood, this.maxMood, this.minMood);

		if (CurrentTurn > 1)
		{
			if (moodLevel < 0)
				this.Building += -10 - Mathf.Log (-moodLevel + 1) * 20;
			else
				this.Building += -10;
			}

		this.Building = this.MarkOff (this.Building, this.maxBuilding, this.minBuilding);
	}


	private IEnumerator CheckNewObjectives(){

		int complete = 0;
		// For every objectives
		for(int i =0; i<this.Objectifs.Count; i++){

			// If the objective is not yet complete
			if(!this.Objectifs[i].Done){
				// If it check
				if(this.Objectifs[i].CheckIfSuccess(this, this.LastActions)){
					complete ++;

					this.ShowObjectives(new string[2] {this.Objectifs[i].Title, this.Objectifs[i].Description});

					yield return new WaitForSeconds(3f);

					this.GUIObjectives.SetActive(false);

				}

			}
			else{
				complete ++;
			}

		}

		this.ShowObjectives(new string[2] {"Objectives", complete+" completed on "+this.Objectifs.Count});
		yield return new WaitForSeconds(3f);

		this.GUIObjectives.SetActive(false);

	}


	public void Reset()
	{
		this.CurrentTurn = 1;
		this.Building = 90;
		this.Population = 100;
		this.Mood = -70;

		buildValue.transform.localPosition = new Vector3 (-0.49f +0.05f*(Mathf.Floor(Mathf.Max(this.Building / 100, 1))), 0, 3);
		buildValue.transform.localScale = new Vector3 ((Mathf.Floor(Mathf.Max(this.Building / 100, 1))) / 10, 1, 1);
		popValue.transform.localPosition = new Vector3 (-0.49f +0.0005f*this.Population, 0, 3);
		popValue.transform.localScale = new Vector3 (this.Population / this.maxPop, 1f, 1f);
		if (this.Mood < -200)
			moodValue.transform.localPosition = new Vector3 (-0.44f, 0, 3);
		else if (this.Mood < -100)
			moodValue.transform.localPosition = new Vector3 (-0.22f, 0, 3);
		else if (this.Mood < 100)
			moodValue.transform.localPosition = new Vector3 (0f, 0, 3);
		else if (this.Mood < 300)
			moodValue.transform.localPosition = new Vector3 (0.22f, 0, 3);
		else if (this.Mood >= 300)
			moodValue.transform.localPosition = new Vector3 (0.44f, 0, 3);
		this.LastActions.Clear ();
		endgameGUIContainer.SetActive (false);
		ingameGUIContainer.SetActive (true);

		GameObject.Find ("Village").GetComponent<Village>().Reset();
		this.ManagerHabitant.UpdateNombreHabitantInstant ();
		this.ManagerHabitant.UpdateMoodHabitants();

		turn.guiText.text = "Turn " + CurrentTurn;
		turn.SetActive (true);
		StartCoroutine ("TurnToChoiceGUI");
	}

	private float MarkOff(int val, int max, int min) { return Mathf.Floor(Mathf.Max(min, Mathf.Min(max, val))); }
	private float MarkOff(float val, float max, float min) { return Mathf.Max(min, Mathf.Min(max, val)); }
}